亲手教你改PE文件之加载基址的新玩法
走过路过,不要错过这个公众号哦!
1.文章简介:
通过本文,读者可以了解PE文件的PE头
组成结构,以及动手修改镜像基地址和大小,来理解对整个PE文件的影响,本文通过对系统自带工具calc,测试DLL等的修改,让大家理解PE的结构。
2.所需知识:
OD
Ollydbg通常称作OD,是一个32位汇编级Microsoft的Windows的分析调试器,汇编代码的动态分析工具,特别是在源代码不可用的情况下非常有用。
Winhex
是一款16进制编辑器。它可以用来检查和修复各种文件、恢复删除文件、硬盘损坏造成的数据丢失等。同时它还可以让你看到其他程序隐藏起来的文件和数据。
关于PE文件的一些基础知识
PE全称为portable executable,是微软WindwosNT,Windows95和Win32子集中的可执行的二进制文件的格式;在WindowsNT中,驱动程序也是这种格式。它还能被应用于各种目标文件和库文件中。Win32 SDK中包含一个名叫<winnt.h>的头文件,其中含有很多用于PE格式的#define和typedef定义。
PE头总览
PE头分为5个部分,如下表
(DOS-头)
DOS-stub
(文件头)
file-header
(可选头)
optional header
(数据目录)
data directories
(节头)
section headers
在一个PE文件的开始处,我们会看到一个MS-DOS可执行体(英语叫“stub”,意为“根,存根”);它使任何PE文件都是一个有效的MS-DOS可执行文件。
在DOS-根之后是一个32位的签名以及魔数0x00004550 (IMAGE_NT_SIGNATURE)(意为“NT签名”,也就是PE签名;十六进制数45和50分别代表ASCII码字母E和P----译者注)。
之后是文件头(按COFF格式),用来说明该二进制文件将运行在何种机器之上、分几个区段、链接的时间、是可执行文件还是DLL
42 35583 42 15232 0 0 2277 0 0:00:15 0:00:06 0:00:09 2923等等。(本文中可执行文件和DLL文件的区别在于:DLL文件不能被启动,但能被别的二进制文件使用,而一个二进制文件则不能链接到另一个可执行文件。)
那些之后,是可选头(尽管它一直都存在,却仍被称作“可选”----因为COFF文件格式仅为库文件使用一个“可选头”,却不为目标文件使用一个“可选头”,这就是为什么它被称为“可选”的原因)。它会告诉我们该二进制文件怎样被载入的更多信息:开始的地址呀、保留的堆栈数呀、数据段的大小呀、等等。
可选头的一个有趣的部分是尾部的“数据目录”数组;这些目录包含许多指向各“节”数据的指针。例如:如果一个二进制文件拥有一个输出目录,那么你就会在数组成员“IMAGE_DIRECTORY_ENTRY_EXPORT”(输出目录项)中找到一个指向那个目录的指针,而该指针指向文件中的某节。
跟在各种头后面我们就发现各个“节”了,它们都由“节头”引导。本质上讲,各节中的内容才是你执行一个程序真正需要的东西,所有头和目录这些东西只是为了帮助你找到它们。
每节都含有和对齐、包含什么样的数据(如“已初始化数据”等等)、是否能共享等有关的一些标记,还有就是数据本身。大多数(并非所有)节都含有一个或多个可通过可选头的“数据目录”数组中的项来参见的目录,如输出函数目录和基址重定位目录等。无目录形式的内容有:例如“可执行代码”或“已初始化数据”等。
DOS-根和签名(DOS-stub and Signature)
DOS-根的概念很早从16位windows的可执行文件(当时是“NE”格式)时就广为人知了。根原来是用于OS/2系统的可执行文件的,也用于自解压档案文件和其它的应用程序。对于PE文件来说,它是一个总是由大约100个字节所组成的和MS-DOS 2.0兼容的可执行体,用来输出象“this program needs windows NT”之类的错误信息。
所有的PE文件都是以一个64字节的DOS头开始。这个DOS头只是为了兼容早期的DOS操作系统。这里不做详细讲解。只需要了解一下其中几个有用的数据。
e_magic:DOS头的标识,为4Dh和5Ah。分别为字母MZ。
e_lfanew:一个双字数据,为PE头的离文件头部的偏移量。Windows加载器通过它可以跳过DOS Stub部分直接找到PE头。
DOS头后跟一个DOS Stub数据,是链接器链接执行文件的时候加入的部分数据,一般是“This program must be run under Microsoft Windows”。这个可以通过修改链接器的设置来修改成自己定义的数据。
你可以通过确认DOS-头部分是否为一个IMAGE_DOS_HEADER(DOS头)结构来认出DOS-根,它的前两个字节必须为连续的两个字母“MZ”。
你可以通过跟在后面的签名来将一个PE二进制文件和其它含有根的二进制文件区分开来,跟在后面的签名可由头成员'e_lfanew'(它是从字节偏移地址60处开始的,有32字节长)所设定的偏移地址找到。对于OS/2系统和Windows系统的二进制文件来说,签名是一个16位的word单元;对于PE文件来说,它是一个按照8位字节边界对齐的32位的longword单元,并且IMAGE_NT_SIGNATURE(NT签名)的值已由#defined定义为0x00004550。
文件头(File Header)
要到达IMAGE_FILE_HEADER(文件头)结构,请先确认DOS-头“MZ”(起始的2个字节),然后找出DOS-根的头部的成员“e_lfanew”,并从文件开始处跳过那么多的字节。在核实你在那里找到的签名后,IMAGE_FILE_HEADER(文件头)结构的文件头就紧跟其后开始了。
1)第一个成员是“Machine(机器)”,一个16位的值,用来指出该二进制文件预定运行于什么样的系统。
2)然后是“NumberOfSections(节数)”成员,16位的值。它是紧跟在头后面的节的数目。我们以后将讨论节的问题。
3)下一个成员是时间戳“TimeDateStamp”(32位),用来给出文件建立的时间。即使它的“官方”版本号没有改变,你也可通过这个值来区分同一个文件的不同版本。(除了同一个文件的不同版本之间必须唯一,时间戳的格式没有明文规定,但似乎是按照UTC?时间“从1970年1月1日00:00:00算起的秒数值”----也就是大多数C语言编译器给time_t标志使用的格式。)
4)成员“PointerToSymbolTable(符号表指针)”和成员“NumberOfSymbols(符号数)”(都是32位)都用于调试信息的。
5)成员“SizeOfOptionalHeader(可选头大小)”(16位)只是“IMAGE_OPTIONAL_HEADER(可选头)”项的大小,你能用它去验证PE文件结构的正确性。
6)成员“Characteristics(特性)”是一个16位的,由许多标志位形成的集合组成,但大多数标志位只对目标文件和库文件有效。具体如下:
位0 IMAGE_FILE_RELOCS_STRIPPED(重定位被剥离文件) 表示如果文件中没有重定位信息,该位置1,这就表明各节的重定位信息都在它们各自的节中;可执行文件不使用该位,它们的重定位信息放在下面将要描述的“base relocation”(基址重定位)目录中。
ImageBase
PE文件被加载到内存的期望的基地址。对于EXE文件,通常加载后的地址就期望的地址。但是DLL却可能是其他的。因为如果这个地址被占,系统就会重新分配一块新的内存,同时会修改此处加载后的地址。EXE文件通常是400000h.DLL通常是10000000h。
3.所需环境环境及具:
Windows XP SP2系统
辅助工具:stud_pe、winhex、OD、系统计算器calc.exe
4.通过winhex查看头信息并定位到文件头和stud_pe比较是否正确:
1. 用winhex打开calc文件。
2.在文件偏移60个字节处,找到e_lfanew字段值,如上图第二个红框标示的值为F0.该值表示偏移值,所以文件头位置如下图所示。
3. 用stud_pe工具打开calc文件,查看文件位置信息,对比是否准确。打开stud_pe,加载calc,点击红色箭头指示的按钮,会出现详细信息。
从图中可以看到,文件头地址也是在F0处。说明我们的定位思路是正确的,此方法是作为信息定位的一个基本方法,在刚开始学习的时候,可以通过这两个工具来学习,加深理解。
5.通过stud_pe修改可执行文件EXE的imageBase基地址,用OD打开查看其中的影响。
1、 用OD,打开calc,执行。
图中可以看到基址01开头,按F9执行后
计算机器运行成功。
2、用stud_pe打开calc文件,定位到imageBase字段。
3、 修改imageBase字段值。并保存。
由0x01000000改成0x60000000,点击按扭save to File。
4、 用OD打开修改后的calc文件,并执行。
上图可知,基址变为06开头,按F9执行。
执行出错,可以看到在读取01002020地址值时出错,因为我们改变了基址,所以01002020地址是不存在的,导致访问违规出错。这里就涉及到重定位表信息,具体可以实验后,自己参考以上实验进行尝试。
6. 通过stud_pe修改DLL文件imageBase基地址,用OD打开查看其中的影响:
1、 用OD打开测试DLL
看到加载基址为10开头。并且弹出对话框。
2、用stud_pe打开测试DLL文件,定位到imageBase字段。
3、修改imageBase字段值,并保存。
由0x10000000改成0x60000000,点击按扭save to File。
4、用OD打开修改后的修改后的DLL文件,并执行。
执行出错,跟EXE是一样的,可以看到在读取1000913C地址值时出错,因为我们改变了基址,所以1000913C地址是不存在的,导致访问违规出错。
5、用OD打开修改前DLL文件,并执行。执行成功后,再加载修改后的DLL文件。
这是第一个DLL加载完成,成功弹出对话框,
这是修改后弹出的DLL,成功弹出。
修改前和修改后的DLL的加载基址对比。通过对比 修改前和修改后的DLL的加载基址发现:第一次加载的dlltest.dll的基址为10000000,第二次加载的复件_dlltest.dll加载基址为60000000,复件_dlltest.dll的重定位数据的通过dll_test加载了。所以能成功加载。
7.分析与思考:
今天给大家讲解了,通过修改PE的加载基址对运行的影响,让我们了解在没有源代码的情况下,安全人员通过一些个工具,还是可以做很多事情的,安全本身就需要发散思维的,希望大家举一反三。
别忘了投稿哟!!!
合天公众号开启原创投稿啦!!!
大家有好的技术原创文章。
欢迎投稿至邮箱:edu@heetian.com;
合天会根据文章的时效、新颖、文笔、实用等多方面评判给予100元-500元不等的稿费哟。
有才能的你快来投稿吧!
合天智汇
网址 : www.heetian.com
电话:4006-123-731
长按图片,据说只有高颜值学霸能识别哦→